package org.springframework.orm.hibernate3.annotation;
import java.io.IOException;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.MappedSuperclass;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.cfg.Configuration;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.orm.hibernate3.LocalSessionFactoryBean;
import org.springframework.util.ClassUtils;
public class AnnotationSessionFactoryBean extends LocalSessionFactoryBean implements ResourceLoaderAware {
	private static final String RESOURCE_PATTERN = "**/*.class";
	private Class[] annotatedClasses;
	private String[] annotatedPackages;
	private String[] packagesToScan;
	private TypeFilter[] entityTypeFilters = {new AnnotationTypeFilter(Entity.class, false),
			new AnnotationTypeFilter(Embeddable.class, false), new AnnotationTypeFilter(MappedSuperclass.class, false)};
	private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
	public AnnotationSessionFactoryBean() {
		setConfigurationClass(AnnotationConfiguration.class);
	}
	public void setConfigurationClass(Class configurationClass) {
		if ((configurationClass == null) || (!AnnotationConfiguration.class.isAssignableFrom(configurationClass))) {
			throw new IllegalArgumentException("AnnotationSessionFactoryBean only supports AnnotationConfiguration or subclasses");
		}
		super.setConfigurationClass(configurationClass);
	}
	public void setAnnotatedClasses(Class[] annotatedClasses) {
		this.annotatedClasses = annotatedClasses;
	}
	public void setAnnotatedPackages(String[] annotatedPackages) {
		this.annotatedPackages = annotatedPackages;
	}
	public void setPackagesToScan(String[] packagesToScan) {
		this.packagesToScan = packagesToScan;
	}
	public void setEntityTypeFilters(TypeFilter[] entityTypeFilters) {
		this.entityTypeFilters = entityTypeFilters;
	}
	public void setResourceLoader(ResourceLoader resourceLoader) {
		this.resourcePatternResolver = (resourceLoader != null
				? ResourcePatternUtils.getResourcePatternResolver(resourceLoader)
				: new PathMatchingResourcePatternResolver());
	}
	protected void postProcessMappings(Configuration config) throws HibernateException {
		AnnotationConfiguration annConfig = (AnnotationConfiguration) config;
		if (this.annotatedClasses != null) {
			for (int i = 0; i < this.annotatedClasses.length; i++) {
				annConfig.addAnnotatedClass(this.annotatedClasses[i]);
			}
		}
		if (this.annotatedPackages != null) {
			for (int i = 0; i < this.annotatedPackages.length; i++) {
				annConfig.addPackage(this.annotatedPackages[i]);
			}
		}
		scanPackages(annConfig);
	}
	protected void scanPackages(AnnotationConfiguration config) {
		if (this.packagesToScan != null)
			try {
				for (String pkg : this.packagesToScan) {
					String pattern = "classpath*:" + ClassUtils.convertClassNameToResourcePath(pkg) + "**/*.class";
					Resource[] resources = this.resourcePatternResolver.getResources(pattern);
					MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);
					for (Resource resource : resources) {
						if (resource.isReadable()) {
							MetadataReader reader = readerFactory.getMetadataReader(resource);
							String className = reader.getClassMetadata().getClassName();
							if (matchesFilter(reader, readerFactory))
								config.addAnnotatedClass(this.resourcePatternResolver.getClassLoader().loadClass(className));
						}
					}
				}
			} catch (IOException ex) {
				throw new MappingException("Failed to scan classpath for unlisted classes", ex);
			} catch (ClassNotFoundException ex) {
				throw new MappingException("Failed to load annotated classes from classpath", ex);
			}
	}
	private boolean matchesFilter(MetadataReader reader, MetadataReaderFactory readerFactory) throws IOException {
		if (this.entityTypeFilters != null) {
			for (TypeFilter filter : this.entityTypeFilters) {
				if (filter.match(reader, readerFactory)) {
					return true;
				}
			}
		}
		return false;
	}
	protected final void postProcessConfiguration(Configuration config) throws HibernateException {
		postProcessAnnotationConfiguration((AnnotationConfiguration) config);
	}
	protected void postProcessAnnotationConfiguration(AnnotationConfiguration config) throws HibernateException {
	}
}